package com.leyou.search.service.Impl;

import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.vo.PageResult;
import com.leyou.client.ItemClient;
import com.leyou.search.dto.GoodsDTO;
import com.leyou.search.dto.SearchRequest;
import com.leyou.search.pojo.*;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.IndexService;
import com.leyou.search.service.IsearchService;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements IsearchService {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private IndexService indexService;

    @Override
    public PageResult<GoodsDTO> search(SearchRequest request) {
        String key = request.getKey();
        if (StringUtils.isBlank(key)) {
            throw new LyException(ExceptionEnum.INVALID_PARAM_ERROR);
        }
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id", "subTitle", "skus"}, null));

        queryBuilder.withQuery(buildBasicQuery(request));

        int page = request.getPage() - 1;
        int size = request.getSize();

        queryBuilder.withPageable(PageRequest.of(page, size));

        AggregatedPage<Goods> goods = elasticsearchTemplate.queryForPage(queryBuilder.build(), Goods.class);

        long total = goods.getTotalElements();
        int totalPage = goods.getTotalPages();
        List<Goods> list = goods.getContent();

        List<GoodsDTO> goodsDTOS = BeanHelper.copyWithCollection(list, GoodsDTO.class);
        return new PageResult<GoodsDTO>(total, totalPage, goodsDTOS);
    }

    @Override
    public Map<String, List<?>> queryFilters(SearchRequest request) {

        Map<String, List<?>> map = new LinkedHashMap<>();

        //查询构建工具
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        queryBuilder.withQuery(buildBasicQuery(request));

        //不查询
        queryBuilder.withPageable(PageRequest.of(0, 1));
        //过滤字段,什么都不要
        queryBuilder.withSourceFilter(new FetchSourceFilterBuilder().build());

        //添加聚合
        //分类聚合
        String categoryAgg = "categoryAgg";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAgg).field("categoryId"));
        //品牌聚合
        String brandAgg = "brandAgg";
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAgg).field("brandId"));

        AggregatedPage<Goods> goodsAggregatedPage = this.elasticsearchTemplate.queryForPage(queryBuilder.build(), Goods.class);

        Aggregations aggregations = goodsAggregatedPage.getAggregations();

        //根据聚合名称获取到聚合
        LongTerms categoryTerms = aggregations.get(categoryAgg);

        List<Long> categoryIds = categoryTerms.getBuckets()
                .stream()
                .map(LongTerms.Bucket::getKeyAsNumber)
                .map(Number::longValue)
                .collect(Collectors.toList());

        List<CategoryDTO> categoryDTOS = itemClient.queryByIds(categoryIds);

        map.put("分类", categoryDTOS);

        LongTerms brandTerms = aggregations.get(brandAgg);

        List<Long> brandIds = brandTerms.getBuckets()
                .stream()
                .map(LongTerms.Bucket::getKeyAsNumber)
                .map(Number::longValue)
                .collect(Collectors.toList());

        List<BrandDTO> brandDTOS = itemClient.queryBrandByIds(brandIds);
        map.put("品牌", brandDTOS);

        //TODO 处理可搜索的规格参数,前提分类是唯一的
        if (categoryIds != null && 1 == categoryIds.size()) {

            handlerAggSpecs(categoryIds.get(0), buildBasicQuery(request), map);


        }
        return map;
    }

    //处理可搜索规格参数的聚合,这个聚合一一定要和,搜索,相关,聚合的可搜索规格字段的值,必须是搜索的结果中的内容,而不是所以内容

    private void handlerAggSpecs(Long cid, QueryBuilder buildBasicQuery, Map<String, List<?>> request) {
        //先查询可搜索规格参数
        List<SpecParamDTO> specParamDTOS = itemClient.querySpecParams(null, cid, true);

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //先执行查询,搜索,对搜索到的数据进行聚合,之前是所有的都聚合
        queryBuilder.withQuery(buildBasicQuery);

        queryBuilder.withSourceFilter(new FetchSourceFilterBuilder().build());

        queryBuilder.withPageable(PageRequest.of(0, 1));

        //循环添加聚合条件
        specParamDTOS.forEach(specParamDTO -> {
            String name = specParamDTO.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(name).field("specs." + name + ".keyword"));
        });
        //执行查询聚合业务
        AggregatedPage<Goods> goodsAggregatedPage = this.elasticsearchTemplate.queryForPage(queryBuilder.build(), Goods.class);
        //获取所有的聚合
        Aggregations aggregations = goodsAggregatedPage.getAggregations();
        specParamDTOS.forEach(specParamDTO -> {
            String name = specParamDTO.getName();
            StringTerms stringTerms = aggregations.get(name);
            List<String> value = stringTerms.getBuckets().stream().map(StringTerms.Bucket::getKeyAsString).collect(Collectors.toList());
            request.put(name, value);

        });

    }

    //查询条件抽取
    private QueryBuilder buildBasicQuery(SearchRequest request) {

        BoolQueryBuilder queryBuilder = new BoolQueryBuilder();

        queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND));

        Set<Map.Entry<String, String>> entrySet = request.getFilter().entrySet();

        entrySet.forEach(entry -> {
            String key = entry.getKey();

            String value = entry.getValue();

            if ("品牌".equals(key)) {
                key = "brandId";
            } else if ("分类".equals(key)) {
                key = "categoryId";
            } else {
                key = "specs." + key + ".keyword";
            }
            queryBuilder.filter(QueryBuilders.termQuery(key, value));
        });


        return queryBuilder;
    }

    public void createIndex(Long id){
        // 查询spu
        SpuDTO spu = itemClient.querySpuById(id);
        // 构建成goods对象
        Goods goods = indexService.buildGoods(spu);
        // 保存数据到索引库
        goodsRepository.save(goods);
    }

    public void deleteById(Long id) {
        goodsRepository.deleteById(id);
    }
}
